/*-------------------------------------------------------------------------
 *
 * pg_statistic.h
 *	  "统计" 系统目录 (pg_statistic) 的定义
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/catalog/pg_statistic.h
 *
 * NOTES
 *	  Catalog.pm 模块读取此文件并推导模式信息。
 *
 *-------------------------------------------------------------------------
 */
#ifndef PG_STATISTIC_H
#define PG_STATISTIC_H

#include "catalog/genbki.h"
#include "catalog/pg_statistic_d.h"

/* ----------------
 *		pg_statistic 定义。 cpp 将其转换为
 *		typedef struct FormData_pg_statistic
 * ----------------
 */
CATALOG(pg_statistic,2619,StatisticRelationId)
{
	/* 这些字段构成该条目的唯一键： */
	Oid			starelid BKI_LOOKUP(pg_class);	/* 包含关系
												 * 属性 */
	int16		staattnum;		/* 属性（列）统计的对象 */
	bool		stainherit;		/* 如果包含继承子类则为真 */

	/* 列条目中为 NULL 的比例： */
	float4		stanullfrac;

	/*
	 * stawidth 是非空条目的平均宽度（以字节为单位）。对于
	 * 固定宽度的数据类型，这当然与 typlen 相同，但对于
	 * 可变宽度类型，这更有用。请注意，这是真正存储的数据的
	 * 平均宽度，经过 TOAST 处理后（例如，对于移动到
	 * 行外的值，仅计算指针对象的大小）。这是统计数据主要用途的
	 * 适当定义，即估算内存中元组哈希表的大小。
	 */
	int32		stawidth;

	/* ----------------
	 * stadistinct 表示列中不同非空数据值的（近似）数量。解释为：
	 *		0		未知或未计算
	 *		> 0		实际不同值的数量
	 *		< 0		行数乘数的负数
	 * 特殊负数情况使我们能够处理列是唯一的（stadistinct = -1）
	 * 或几乎唯一（例如，非空值平均出现约两次的列，可以用
	 * stadistinct = -0.5 表示，如果没有 NULL，或者如果
	 * 20% 的列是 NULL，则为 -0.4）。因为 pg_class 中的行数统计
	 * 可能会比 pg_statistic 更新得更频繁，所以能够以行数的倍数
	 * 来描述这类情况，而不是固定的不同值数量非常重要。但在
	 * 其他情况下，固定数字是正确的（例如，一个布尔列）。
	 * ----------------
	 */
	float4		stadistinct;

	/* ----------------
	 * 为了允许对不同类型的数据类型保持统计信息，
	 * 我们不为其余统计字段硬编码任何特定含义。
	 * 相反，我们提供几个“槽”以便放置统计数据。
	 * 每个槽包括：
	 *		kind			标识数据类型的整数代码（见下文）
	 *		op				相关操作符的 OID（如需要）
	 *		coll			相关排序规则的 OID，若无则为 0
	 *		numbers			float4 数组（用于统计值）
	 *		values			anyarray（用于数据值的表示）
	 * ID、操作符和排序规则字段永远不为 NULL；它们在未使用的
	 * 槽中为零。在未使用的槽中，numbers 和 values 字段为 NULL，
	 * 如果槽的类型不需要其中一个或另一个，它们在使用的槽中
	 * 也可能为 NULL。
	 * ----------------
	 */

	int16		stakind1;
	int16		stakind2;
	int16		stakind3;
	int16		stakind4;
	int16		stakind5;

	Oid			staop1 BKI_LOOKUP_OPT(pg_operator);
	Oid			staop2 BKI_LOOKUP_OPT(pg_operator);
	Oid			staop3 BKI_LOOKUP_OPT(pg_operator);
	Oid			staop4 BKI_LOOKUP_OPT(pg_operator);
	Oid			staop5 BKI_LOOKUP_OPT(pg_operator);

	Oid			stacoll1 BKI_LOOKUP_OPT(pg_collation);
	Oid			stacoll2 BKI_LOOKUP_OPT(pg_collation);
	Oid			stacoll3 BKI_LOOKUP_OPT(pg_collation);
	Oid			stacoll4 BKI_LOOKUP_OPT(pg_collation);
	Oid			stacoll5 BKI_LOOKUP_OPT(pg_collation);

#ifdef CATALOG_VARLEN			/* 可变长度字段从这里开始 */
	float4		stanumbers1[1];
	float4		stanumbers2[1];
	float4		stanumbers3[1];
	float4		stanumbers4[1];
	float4		stanumbers5[1];

	/*
	 * 这些数组中的值是列数据类型的值，或某些
	 * 相关类型的值，例如数组元素类型。我们目前必须
	 * 相当多地作弊以允许这种多态数组，但也许
	 * 有一天它会变得不那么虚假。
	 */
	anyarray	stavalues1;
	anyarray	stavalues2;
	anyarray	stavalues3;
	anyarray	stavalues4;
	anyarray	stavalues5;
#endif
} FormData_pg_statistic;

#define STATISTIC_NUM_SLOTS  5


/* ----------------
 *		Form_pg_statistic 对应于具有 pg_statistic
 *		关系格式的元组的指针。
 * ----------------
 */
typedef FormData_pg_statistic *Form_pg_statistic;

DECLARE_TOAST(pg_statistic, 2840, 2841);

DECLARE_UNIQUE_INDEX_PKEY(pg_statistic_relid_att_inh_index, 2696, StatisticRelidAttnumInhIndexId, on pg_statistic using btree(starelid oid_ops, staattnum int2_ops, stainherit bool_ops));

DECLARE_FOREIGN_KEY((starelid, staattnum), pg_attribute, (attrelid, attnum));

#ifdef EXPOSE_TO_CLIENT_CODE

/*
 * 核心 PostgreSQL 定义了几种统计插槽“类型”，
 * 如下文所述。此外，自定义数据类型可以通过
 * 自定义 typanalyze 例程与类型操作符的
 * 选择性估算函数之间的相互协议来定义
 * 自己的“类型”代码。
 *
 * 读取 pg_statistic 关系的代码不应假设特定的
 * 数据“类型”会出现在任何特定插槽中。
 * 相反，搜索 stakind 字段以查看所需的数据是否可用。
 * （标准函数 get_attstatsslot() 可用于此。）
 */

/*
 * 当前“类型”代码的分配如下：
 *
 * 	1-99：		保留用于核心 PostgreSQL 项目的分配
 * 				（此范围内的值将在本文件中记录）
 * 	100-199：	保留用于 PostGIS 项目的分配
 * 				（将在 PostGIS 文档中记录的值）
 * 	200-299：	保留用于 ESRI ST_Geometry 项目的分配
 * 				（将在 ESRI ST_Geometry 文档中记录的值）
 * 	300-9999：	保留用于未来公共分配
 *
 * 对于私人使用，您可以在范围
 * 10000-30000 内随机选择一个“类型”代码。
 * 但是，对于广泛传播的代码，最好通过
 * 请求 PostgreSQL 全球开发小组获取一个公共定义的“类型”代码。
 */

/*
 * 在“最常见值”插槽中，staop 是用来决定值是否相同的“=”
 * 操作符的 OID，stacoll 是使用的排序规则
 * （与列的排序规则相同）。stavalues 包含
 * 列中出现的 K 个最常见的非空值，而 stanumbers
 * 包含它们的频率（占总行数的比例）。这些值
 * 应按频率降序排列。请注意，由于数组是可变大小的，
 * K 可能由统计收集器选择。值不应
 * 出现在 MCV 中，除非它们已被观察到出现不止一次；
 * 唯一列将没有 MCV 插槽。
 */
#define STATISTIC_KIND_MCV	1

/*
 * “直方图”插槽描述标量数据的分布。staop 是
 * 描述排序顺序的“<”操作符的 OID，stacoll
 * 是相关的排序规则。（在理论上，如果数据类型具有多个有用的排序操作符，
 * 或者我们关心多个排序规则，则可能出现多个直方图。
 * 当前的排序规则将始终是基础列的排序规则。）stavalues
 * 包含 M（>=2）个非空值，这些值将非空列数据值划分为
 * M-1 个大致相等的组。第一个 stavalues 项是最小值，最后一个是最大值。
 * stanumbers 不使用，应为 NULL。重要事项：如果提供了 MCV
 * 插槽，则直方图描述数据分布
 * *在去除 MCV 中列出的值之后*（因此，这是一个“压缩
 * 直方图”用技术术语描述）。这允许更准确地
 * 表示包含一些非常常见值的列的分布。
 * 在只有少数不同值的列中，MCV 列表可能描述整个数据总体；
 * 在这种情况下，直方图会减少为空，并应省略。
 */
#define STATISTIC_KIND_HISTOGRAM  2

/*
 * “相关性”插槽描述表元组的物理顺序
 * 与此列数据值的顺序之间的相关性，如
 * 通过 staop 确定的“<”操作符和由 stacoll
 * 确定的排序规则所见。（与直方图一样，理论上可以出现多个
 * 条目。）stavalues 不使用，应为 NULL。stanumbers 包含
 * 单个条目，即数据值序列与其实际元组位置
 * 序列之间的相关系数。系数范围从 +1 到 -1。
 */
#define STATISTIC_KIND_CORRELATION	3


/*
 * 一个“最常见元素”槽类似于“最常见值”槽，除了它存储列值中最常见的非空*元素*。当列的数据类型是数组或其他具有可识别元素的类型（例如，tsvector）时，这一点很有用。staop包含适合元素类型的相等运算符，stacoll包含要与之使用的排序规则。stavalues包含最常见的元素值，stanumbers包含它们的频率。与MCV槽不同，频率是以元素值出现的非空行的比例来衡量的，而不是所有行的频率。同样，与MCV槽不同，值根据元素类型的默认顺序排序（以支持特定值的二进制搜索）。由于这使得stanumbers中的最小和最大频率位于不可预测的位置，因此stanumbers还有两个额外成员，保存最小和最大频率的副本。可选地，可以有第三个额外成员，保存空元素的频率（以相同的方式表达：包含至少一个空元素的非空行的比例）。如果省略此成员，则假定该列不包含空元素。
 *
 * 注意：在当前使用的tsvector列中，stavalues元素的类型为text，尽管它们在tsvector中的表示并不完全是text。
 */
#define STATISTIC_KIND_MCELEM  4

/*
 * 一个“不同元素计数直方图”槽描述了数组类型列中每行存在的不同元素值数量的分布。只考虑非空行和非空元素。staop包含适合元素类型的相等运算符，stacoll包含要与之使用的排序规则。stavalues未使用，应为NULL。stanumbers的最后一个成员是所有非空行中不同元素值的平均计数。前面的M（>=2）个成员形成一个直方图，将不同元素计数的人群分成大约相等的人口的M-1个区间。第一个是观察到的最小计数，最后一个是最大计数。
 */
#define STATISTIC_KIND_DECHIST	5

/*
 * 一个“长度直方图”槽描述了范围类型列中行的范围长度的分布。stanumbers包含一个条目，即空范围的比例。stavalues是非空长度的直方图，格式类似于STATISTIC_KIND_HISTOGRAM：它包含M（>=2）个范围值，将列数据值分成大约相等人口的M-1个区间。长度以float8的形式存储，按范围类型的subdiff函数测量。只考虑非空行。
 */
#define STATISTIC_KIND_RANGE_LENGTH_HISTOGRAM  6

/*
 * 一个“边界直方图”槽类似于STATISTIC_KIND_HISTOGRAM，但用于范围类型列。stavalues包含M（>=2）个范围值，将列数据值分成大约相等人口的M-1个区间。与常规标量直方图不同，这实际上是两个直方图组合成一个数组，每个值的下界形成一个下界的直方图，而上界则形成一个上界的直方图。仅包括非空、非空的范围。
 */
#define STATISTIC_KIND_BOUNDS_HISTOGRAM  7

#endif							/* EXPOSE_TO_CLIENT_CODE */

#endif							/* PG_STATISTIC_H */
